#include "action.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

char * TAB_TYPE[4] = {"i32","float","i8*","i1"};
int lab = 0;

/*
if (!estType($1,UNDEF) && !memeType($3,$1))
     {
       fprintf(stderr, "Incompatible type\n");
     }
   else if (estType($1,UNDEF))
     {
       $1->type = $3->type;
     }
   switch($3->type)
     {
     case INTEGER:
       if($3->nom == NULL){
	 if($1->portee==GLOBAL)
	   printf("@%s = add i32 %d, 0 \n",$1->nom, $3->val.i);
	 else if($1->portee == LOCAL)
	   printf("%%%s = add i32 %d, 0 \n",$1->nom, $3->val.i);
       }
     break;
     case FLOAT:
       if($3->nom == NULL){
	 if($1->portee==GLOBAL)
	   printf("@%s = fadd float %f, 0 \n",$1->nom, $3->val.f);
	 else if($1->portee == LOCAL)
           printf("%%%s = fadd float %f, 0 \n",$1->nom, $3->val.f);
       }
     break;
     }
*/

struct var * actionCONCATE(struct table * tab, struct var * var1, struct var * var2)
{
  struct var* res = creerVar(tab,NULL,EXPR);  
  ajouterCode(res,var1->code);
  ajouterCode(res,var2->code);
  return res;
}

struct var * actionCHARGER(struct table * tab, struct var * var){
  struct var * res = creerVar(tab,NULL,var->type);
  char s[256];
  sprintf(s,"%s = load %s* %s\n", res->reg, TAB_TYPE[res->type], var->reg);
  ajouterCode(res,s);  
  return res;
}

struct var * actionWHILE(struct table * tab, struct var * var1, struct var * var2){
  struct var * res = creerVar(tab,NULL,EXPR);
  char s[256];
  sprintf(s,"L%d:\n",lab);
  ajouterCode(res,s);
  ajouterCode(res,var1->code);
  sprintf(s,"br i1 %s, label %%L%d, label %%L%d \n",var1->reg,lab+1,lab+2);
  ajouterCode(res,s);
  sprintf(s,"L%d:\n",lab+1);
  ajouterCode(res,s);
  ajouterCode(res,var2->code);
  sprintf(s,"br label %%L%d:\n",lab);
  ajouterCode(res,s);
  sprintf(s,"L%d:\n",lab+2);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  lab+=3;
  return res;
}

struct var * actionIF(struct table * tab, struct var * var1, struct var * var2)
{
  struct var * res;
  char s[256];
  res = creerVar(tab,NULL,EXPR);
  sprintf(s,"br i1 %s, label %%L%d, label %%L%d \n",var1->reg,lab+1,lab+2);
  lab+=2;
  ajouterCode(res,var1->code);
  ajouterCode(res,s);
  sprintf(s,"L%d:\n",lab-1);
  ajouterCode(res,s);
  ajouterCode(res,var2->code);
  sprintf(s,"br label %%L%d:\n",lab);
  ajouterCode(res,s);
  sprintf(s,"L%d:\n",lab);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  lab++;
  return res;  
}

struct var * actionANDOR(struct table * tab, struct var * var1, struct var * var2, char * signe){
  if(!memeType(var1,var2) || var1->type == FLOAT || var1->type == STRING || var2->type == FLOAT || var2->type == STRING){
    perror("Wrong AND/OR");
    exit(1);
  }
  struct var * res;
  char s[256];
  res = creerVar(tab,NULL,BOOL);
  sprintf(s,"%s = %s %s %s, %s\n",res->reg,signe,TAB_TYPE[var1->type],var1->reg,var2->reg);
  ajouterCode(res,var1->code);
  ajouterCode(res,var2->code);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}

struct var * actionCOMPARAISON(struct table * tab, struct var * var1, struct var * var2, char * signe){ // FLOAT !!!!
  if(var1->type != INTEGER ||var2->type != INTEGER){
    perror("Wrong comparison");
    exit(1);
  }
  struct var * res;
  char s[256];
  res = creerVar(tab,NULL,BOOL);
  sprintf(s,"%s = icmp %s %s %s, %s\n",res->reg,signe,TAB_TYPE[var1->type],var1->reg,var2->reg);
  ajouterCode(res,var1->code);
  ajouterCode(res,var2->code);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}

struct var * actionSUB(struct table * tab, struct var * var1, struct var * var2)
{
  struct var * res;
  struct var * cast;
  char s[256];
  char s1[256];
  
  if(var1->type == STRING || var2->type == STRING){
    perror("Subition from incompatible type");
    exit(1);
  }
  else if(var1->type == FLOAT && var2->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var2->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fsub %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], var1->reg, cast->reg);
  }
  else if(var2->type == FLOAT && var1->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var1->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fsub %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], cast->reg, var2->reg);
  }
  else if (var1->type == INTEGER){
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = sub %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  else {
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = fsub %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}


struct var * actionADD(struct table * tab, struct var * var1, struct var * var2)
{
  struct var * res;
  struct var * cast;
  char s[256];
  char s1[256];
  
  if(var1->type == STRING || var2->type == STRING){
    perror("Addition from incompatible type");
    exit(1);
  }
  else if(var1->type == FLOAT && var2->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var2->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fadd %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], var1->reg, cast->reg);
  }
  else if(var2->type == FLOAT && var1->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var1->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fadd %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], cast->reg, var2->reg);
  }
  else if (var1->type == INTEGER){
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = add %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  else {
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = fadd %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}


struct var * actionMUL(struct table * tab, struct var * var1, struct var * var2)
{
  struct var * res;
  struct var * cast;
  char s[256];
  char s1[256];
  
  if(var1->type == STRING || var2->type == STRING){
    perror("Multiplication from incompatible type");
    exit(1);
  }
  else if(var1->type == FLOAT && var2->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var2->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fmul %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], var1->reg, cast->reg);
  }
  else if(var2->type == FLOAT && var1->type == INTEGER){
    res = creerVar(tab,NULL,FLOAT);
    cast = creerVar(tab,NULL,FLOAT);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s1,"%s = sitofp %s %s to %s\n",cast->reg,TAB_TYPE[INTEGER],var1->reg,TAB_TYPE[FLOAT]);
    ajouterCode(res,s1);
    sprintf(s,"%s = fmul %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], cast->reg, var2->reg);
  }
  else if (var1->type == INTEGER){
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = mul %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  else {
    res = creerVar(tab,NULL,var1->type);
    ajouterCode(res,var1->code);
    ajouterCode(res,var2->code);
  
    sprintf(s,"%s = fmul %s %s, %s\n", res->reg, TAB_TYPE[var1->type], var1->reg, var2->reg);
  }
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}

struct var * actionDIV(struct table * tab, struct var * var1, struct var * var2)
{
  struct var * res;
  char s[256];
  if(var1->type == STRING || var2->type == STRING){
    perror("Division from incompatible type");
    exit(1);
  }
  res = creerVar(tab,NULL,FLOAT);
  ajouterCode(res,var1->code);
  ajouterCode(res,var2->code);
  sprintf(s,"%s = fdiv %s %s, %s\n", res->reg, TAB_TYPE[FLOAT], var1->reg, var2->reg);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}

struct var * actionAFFECTATION(struct table* tab,struct var* var1, struct var* var2)
{
  if(estType(var1,UNDEF))
    {
      var1->type = var2->type;
    }
  else if(!memeType(var1,var2))
    {
      DebugAfficher(var1);
      DebugAfficher(var2);
      perror("Invalid Type");
      exit(1);
    }

  struct var* res = creerVar(tab,NULL,EXPR);  
  if(var1->allocated ==0)
    {
      char s[256];
      sprintf(s,"%s = alloca  %s \n", var1->reg, TAB_TYPE[var1->type]);
      ajouterCode(res,s);
      var1->allocated =1;
    }
  ajouterCode(res,var1->code);
  ajouterCode(res,var2->code);
  char s[256];
  sprintf(s,"store %s %s, %s* %s\n", TAB_TYPE[var2->type], var2->reg, TAB_TYPE[var1->type], var1->reg);
  ajouterCode(res,s);
  effacerCode(var1);
  effacerCode(var2);
  return res;
}

void actionFLOAT(struct var* var, float f)
{
  char s[256];
  sprintf(s,"%s = fadd float %f, 0.0\n", var->reg, f);
  ajouterCode(var,s);
}

void actionINT(struct var* var, int i)
{
  char s[256];
  sprintf(s,"%s = add i32 %d, 0\n", var->reg, i);
  ajouterCode(var,s);
}
